---
layout: ../../../layouts/PageLayout.astro
title: Handling Forms
description: Handling form state and submissions
order: 2
next:
  path: guide/composition-api/nested-objects-and-arrays
  title: Nested Objects and Arrays
  description: Structuring form values in nested paths in objects or arrays
  intro: |
    So far we've only dealt with flat form values, what about nested objects and arrays? In the next guide you will learn how to use path names to structure your values and nest them declaratively.
---

import DocTip from '@/components/DocTip.vue';
import Repl from '@/components/MdxRepl.vue';

# Handling Forms

vee-validate makes it easy to handle form submissions, resets, and DX to make your forms much easier to reason about and less of a burden to maintain. The `useForm` function allows you to easily handle:

<div class="features">

- Form state (valid/dirty/touched/pending).
- Submitting forms and handling invalid submissions.
- Handling form resets.

</div>

## Form Metadata

Forms have a `meta` object value containing useful information about the form, it acts as an aggregation of the metadata for the fields inside that form.

```js
const { meta } = useForm();

meta.value.dirty;
meta.value.pending;
meta.value.touched;
meta.value.valid;
meta.value.initialValues;
```

- `valid`: The form's validity status, will be `true` if the errors array is empty initially, but will be updated once the form is mounted.
- `touched`: If at least one field was blurred (unfocused) inside the form.
- `dirty`: If at least one field's value was updated.
- `pending`: If at least one field's validation is still pending.
- `initialValues`: All fields' initial values, this is an object where the keys are the field names.

Here is a simple example where we disable the form's submit button unless a field was touched.

<Repl files={{ 'App.vue': 'CompositionHandlingForms01' }} client:visible />

## Handling Submissions

vee-validate exposes useful defaults to help you handle form submissions whether you submit them using JavaScript or native HTML submissions.

### JavaScript Submissions (AJAX)

To handle submissions, you can use the `handleSubmit` function to create submission handlers for your forms, the `handleSubmit` function accepts a callback that receives the final form values.

```js
const { handleSubmit } = useForm({
  validationSchema: yup.object({
    email: yup.string().email().required(),
    password: yup.string().min(6).required(),
  }),
});

// Creates a submission handler
// It validates all fields and doesn't call your function unless all fields are valid
// You can bind `onSubmit` to a form element's submit event, or call it directly to submit the current data.
const onSubmit = handleSubmit(values => {
  alert(JSON.stringify(values, null, 2));
});
```

Here is an example that makes use of `handleSubmit` to validate before submitting the form.

<Repl files={{ 'App.vue': 'CompositionHandlingForms02' }} client:visible />

The `handleSubmit` function will only execute your callback once the returned function (`onSubmit` in the example) if all fields are valid, meaning you don't have to handle if the form is invalid in your logic.

You can call the returned function either manually or via an event like `@submit` and it will validate all the fields and execute the callback if everything passes validation.

As a bonus, when the returned function is used as an event handler (like in the previous example) it will automatically prevent the default submission of the form so you don't need to use the `prevent` modifier like you normally would.

### Full-Page Submissions (non-AJAX)

For non-ajax submissions that trigger a full page reload, you can use the `submitForm` function instead of `handleSubmit`. You normally would use this if you are not building a single-page application. In the following example, we submit the form to another tab using the `get` form method.

<Repl files={{ 'App.vue': 'CompositionHandlingForms13' }} client:visible />

In that case **YOU MUST** use `submitForm` as an event handler for the `submit` event for a native `form` element, otherwise, it would have no effect.

### Handling Invalid Submissions

Sometimes you want to perform some logic after a form fails to submit due to validation errors (e.g. focusing the first invalid field), you can pass a callback as the second argument to the `handleSubmit` function.

```js
const { handleSubmit } = useForm();

function onSuccess(values) {
  alert(JSON.stringify(values, null, 2));
}

function onInvalidSubmit({ values, errors, results }) {
  console.log(values); // current form values
  console.log(errors); // a map of field names and their first error message
  console.log(results); // a detailed map of field names and their validation results
}

// This handles both valid and invalid submissions
const onSubmit = handleSubmit(onSuccess, onInvalidSubmit);
```

Here is a quick example of how to scroll to and focus the first invalid field after a failed submission attempt.

<Repl files={{ 'App.vue': 'CompositionHandlingForms03' }} client:visible />

### Submission Progress

Quite often you need to show your users a submission indicator, or you might want to disable the submit button entirely until the submission attempt is done. The `useForm` function exposes an `isSubmitting` ref that you can use.

The `isSubmitting` state will be set to `true` once the validation of the form starts (as a result of a submit event) and will keep track of the submission handler you passed to either `onSubmit` or until it calls `submitForm`. If the submission handler throws any errors or completes successfully it will be set to `false` afterward.

```js
const { isSubmitting } = useForm();
```

<Repl files={{ 'App.vue': 'CompositionHandlingForms04' }} client:visible />

### Submit Count

The `useForm` function also exposes a `submitCount` ref that you can use to track the number of submissions attempted by the user. The count is incremented regardless of the validation result.

```js
const { submitCount } = useForm();
```

Maybe you want to lock the form if too many attempts were made, or you want to show a message after the first submission attempt.

<Repl files={{ 'App.vue': 'CompositionHandlingForms05' }} client:visible />

### Submission Behavior

vee-validate does the following when calling submission handlers created by `handleSubmit` or when calling `submitForm` as a result of the user submitting the form.

#### Before validation stage

- Sets all fields `touched` meta to `true`
- Sets `isSubmitting` form state to `true`
- Increments the `submitCount` form state by `1`

#### Validation stage

- Sets form and individual fields meta `pending` to `true` to indicate validation is in progress
- Runs the validation function/schema/rule against the current form values asynchronously
- Checks for any errors in the validation result
  - If there are errors then it will skip the next stage and update the validation state (meta, errors) for the form and fields
  - If there aren't any errors then it will set the `pending` meta flag to `false` and proceed to the next stage

#### After validation stage

- Calls the `handleSubmit` handler you passed
- After the callback finishes (it will wait if the result is asynchronous), then it will set `isSubmitting` to `false`

Note that there isn't a need to have `isSubmitting` set back to false if you've used `submitForm`, as this submission method will perform a full-page refresh (native forms behavior).

## Form Values

You may have noticed in the earlier examples that you can access all fields' values using the `values` reactive object returned by `useForm`.

```js
const { values } = useForm();
```

The `values` object is read-only and should not be mutated with a `v-model` or by assigning a value to it. This is because all mutations are done through the vee-validate API, this is because mutations to form state need to have a context.

For example:

```js
const { values } = useForm();

// ❌ Do not do that!
values.email = '';
```

In order for the form UX to be stable, we need to understand why the email value was set to `''`. Was it being reset? Should we run validation again? This is the type of small differences that are a result of vee-validate's design choice based on fields and forms, not values.

### Initial Values

Since you don't have to use `v-model` to track your values, the `useForm` function allows you to define the starting values for your fields, by default all fields start with `undefined` as a value.

Using the `initialValues` option you can send an object that contains the field names as keys and their values:

```js
const { defineInputBinds } = useForm({
  initialValues: {
    email: 'test@example.com',
    password: 'p@$$w0rd',
  },
});
```

<Repl files={{ 'App.vue': 'CompositionHandlingForms06' }} client:visible />

<DocTip>

It's generally recommended that you provide the `initialValues`, this is because vee-validate cannot assume a reasonable initial value for your fields other than `undefined` which may cause unexpected behavior when using a 3rd-party validator that does not deal with `undefined`.

</DocTip>

You can reset initial values at [any time using the `resetForm`](#handling-resets) function returned by `useForm`.

### Manually Setting Form Values

You can set any field's value using either `setFieldValue` or `setValues` returned by `useForm`.

```js
const { setFieldValue, setValues } = useForm();

// Sets a value of a specific field in the form values
setFieldValue('fieldName', 'value');

// Merges the given object with the current form values
setValues({
  fieldName: 'value',
});
```

<Repl files={{ 'App.vue': 'CompositionHandlingForms07' }} client:visible />

### Controlled Values

The form values can be categorized into two categories:

- Controlled values: values that have a form input controlling them via `defineInputBinds` or `defineComponentBinds` or `useField` or `<Field />`.
- Uncontrolled values: values that are inserted dynamically with `setFieldValue` or inserted initially with initial values.

Sometimes you maybe only interested in controlled values. For example, your initial data contains noisy extra properties from your API and you wish to ignore them when submitting them back to your API.

When accessing `values` from `useForm` result or the submission handler you get all the values, both controlled and uncontrolled values. To get access to only the controlled values you can use `controlledValues` from the `useForm` result:

```js
const { handleSubmit, controlledValues } = useForm();

const onSubmit = handleSubmit(async () => {
  // Send only controlled values to the API
  const response = await client.post('/users/', controlledValues.value);
});
```

Alternatively, for less verbosity, you can create submission handlers with only the controlled values with `handleSubmit.withControlled` which has the same API as `handleSubmit`:

```js
const { handleSubmit } = useForm();

const onSubmit = handleSubmit.withControlled(async values => {
  // Send only controlled values to the API
  const response = await client.post('/users/', values);
});
```

Here is an example that filters out some noisy initial values when submitting the form using the `withControlled` modifier.

<Repl files={{ 'App.vue': 'CompositionHandlingForms12' }} client:visible />

### Setting initial values asynchronously

Sometimes your data is fetched asynchronously from an API and you want to set the initial values or the current values after the data is fetched. You can do that by using `resetForm` to set both current and initial data.

<Repl files={{ 'App.vue': 'CompositionHandlingForms14' }} client:visible />

You could alternatively use `setValues` but note that `setValues` can trigger validation and do not reset the meta state for the fields like `dirty` or `touched`.

## Handling Resets

vee-validate also handles form resets in a similar way to submissions. When resetting the form, all fields' errors will be cleared, meta info will be reset to defaults and the values will be reset to their original or initial values.

To reset forms you can use the `resetForm` function returned by `useForm`. You can also reset the form to a new state by passing a `FormState` object to the `resetForm` function. You can then set errors, `touched` meta, and the values.

```js
const { resetForm } = useForm();

// Resets the form
resetForm();

//
resetForm({
  touched: {
    email: false,
  },
  errors: {
    email: 'custom error',
  },
  values: {
    email: 'newvalue@email.com',
  },
});
```

This is the shape of the `FormState` object:

```ts
interface FormState {
  // any error messages
  errors: Record<string, string>;
  // touched meta flags
  touched: Record<string, boolean>;
  // Form Values
  values: Record<string, any>;
}
```

Here is an example where a full form is being reset:

<Repl files={{ 'App.vue': 'CompositionHandlingForms08' }} client:visible />

### Resetting Forms After Submit

Usually, you will reset your forms after successful submission. For convenience, the `onSubmit` handler receives an additional `FormActions` object in the second argument that allows you to do some actions on the form after submissions, this is the shape of the `FormActions` object:

```ts
export interface FormActions {
  setFieldValue: (field: T, value: any) => void;
  setFieldError: (field: string, message: string | undefined) => void;
  setErrors: (fields: Partial<Record<string, string | undefined>>) => void;
  setValues: (fields: Partial<Record<T, any>>) => void;
  setFieldTouched: (field: string, isTouched: boolean) => void;
  setTouched: (fields: Partial<Record<string, boolean>>) => void;
  resetForm: (state?: Partial<FormState>) => void;
}
```

This is an example of using the form actions object to reset the form:

<Repl files={{ 'App.vue': 'CompositionHandlingForms09' }} client:visible />

## Errors

As you have previously seen in some examples, you have access to `errors` with `useForm` that contains a mapping of each field's path and its error message.

```js
const { errors } = useForm();
```

However, if you want to display multiple errors for your fields then you can use `errorBag` which is a mapping of each field's path and an array of error messages for that field.

```js
const { errorBag } = useForm();
```

Here is an example that displays multiple errors for a field:

<Repl files={{ 'App.vue': 'CompositionHandlingForms10' }} client:visible />

### Initial Errors

If you are building a non-SPA application it is very common to pre-fill form errors using server-side rendering, frameworks like Laravel and Rails make this very easy to do. vee-validate supports filling the errors initially before any validation is done using the `initialErrors` option.

The `initialErrors` option accepts an object containing the field names as keys with their corresponding error message string.

```js
useForm({
  initialErrors: {
    email: 'This email is already taken',
    password: 'The password is too short',
  },
});
```

<DocTip>

`initialErrors` are applied once the component that called `useForm` is mounted and is ignored after, so any changes to the `initialErrors` props won't affect the messages.

</DocTip>

### Setting Errors Manually

Quite often you will find yourself unable to replicate some validation rules on the client-side due to natural limitations. For example, `unique` email validation is complex to implement on the client side. So the ability to set errors manually can be useful.

You can set messages for fields by using either `setFieldError` which sets an error message for one field at a time, or by using the `setErrors` function which allows you to set error messages for multiple fields at once.

Both functions are available as a return value from `useForm`. In the following example, we check if the server response contains any validation errors and we set them on the fields:

```js
const { handleSubmit, setFieldError, setErrors } = useForm();

const onSubmit = handleSubmit(async values => {
  // Send data to the API
  const response = await client.post('/users/', values);

  // all good
  if (!response.errors) {
    return;
  }

  // set single field error
  if (response.errors.email) {
    setFieldError('email', response.errors.email);
  }

  // set multiple errors, assuming the keys are the names of the fields
  // and the key's value is the error message
  setErrors(response.errors);
});
```

Alternatively, you can use the `FormActions` passed as the second argument to the `handleSubmit` callback which contains both functions for convenience:

```js
const onSubmit = handleSubmit(async (values, actions) => {
  // Send data to the API
  const response = await client.post('/users/', values);
  // ...

  // set single field error
  if (response.errors.email) {
    actions.setFieldError('email', response.errors.email);
  }

  // set multiple errors, assuming the keys are the names of the fields
  // and the values is the error message
  actions.setErrors(response.errors);
});
```

Here is an example that sets form errors after submission, usually you will have a backend API that returns the errors:

<Repl files={{ 'App.vue': 'CompositionHandlingForms11' }} client:visible />
